﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace ControleEstoque.Entidades
{
	public class Reader6C : Reader
	{
		public string ip;
		public int port;
		private NetworkStream networkstream;
		bool _loop;
		const int TagSize = 19;

		public override bool Connect(string addr, int portn)
		{
			ip = addr;
			port = portn;
			try
			{
				IPEndPoint ipend = new IPEndPoint(IPAddress.Parse(addr), port);
				Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				server.Connect(ipend);
				networkstream = new NetworkStream(server);
				ConfigReaderSys();
				Connected = true;
				return true;

			}
			catch
			{
				return false;
			}
		}

		public override bool Disconnect()
		{
			try
			{
				if (Connected)
				{
					PowerAmplifier("off");
					networkstream.Close();
					Connected = false;
				}
				return true;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public override bool SetReadLoop(byte antenna)
		{
			byte[] read_loop = { 0x00, 0x04, 0x81, 0x00, 0x00, 0x01, 0x15, 0xda };

			try
			{
				if (antenna != 0)
				{
					read_loop[3] = antenna;
					Checksum(read_loop, 6);
				}
				networkstream.Write(read_loop, 0, read_loop.Length);
				_loop = true;
				return true;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public override string Read()
		{
			byte[] buff = new byte[TagSize];
			byte[] crc = new byte[TagSize];
			try
			{
				if (_loop)
				{
					buff = GivemeWhatINeed(0x81);
					// ERROR CHECK
					if (buff[3] == 0x00)
					{
						// Checksum
						crc = buff;
						Checksum(crc, 17);
						if (Misc.Equals(crc, buff))
							return Misc.PrintTag(buff, TagSize);
					}
				}
				return null;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public override string[] Read(int num, byte antenna)
		{
			byte[] read_tag = { 0x00,
								  0x04,
								  0x81,
								  // the antenna from where the EPCN tags will be read
								  antenna,
								  // Solve Q time slot
								  (byte)SolveQ(num),
								  0x00,
								  0x00,
								  0x00 };
			byte[] buff = new byte[num * TagSize]; // Isto significa que cada epcn (tag RFID) terá 19 bytes?
			int size;

			try
			{
				// Checksum
				Checksum(read_tag, 6);

				// Send the command to the RFID Reader hardware
				networkstream.Write(read_tag, 0, read_tag.Length);
				
				// Read the buffer
				size = networkstream.Read(buff, 0, buff.Length);
				string result = buff[3] == 0x00 ? Misc.PrintTag(buff, size) : null; // Qual a necessidade de testar a posição 3ª do vetor?
				StringBuilder sb = new StringBuilder(result);
				string[] toReturn = new string[size / TagSize];
				int i = 0;
				while (sb.Length > 0)
				{
					toReturn[i++] = sb.ToString().Substring(0, TagSize * 2);
					sb.Remove(0, TagSize * 2);
				}

				return toReturn;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public override bool Write(string epcn)
		{
			return Write(epcn, "0");
		}

		public override bool Write(string epcn, string passwd)
		{
			try
			{
				byte[] write_cmd = { 0x00, 0x13, 0x83, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
				byte[] aux = new byte[7];

				/* Turns string (epcn) into a 12 bytes hexadecimal byte array (epc) */
				byte[] epc = Misc.GetBytes(epcn, 12);
				byte[] pass = Misc.GetBytes(passwd, 4);

				write_cmd[3] = 0x00;	/* TODO anetnna */
				for (int i = 0; i < epc.Length; i++)
					write_cmd[(21 - epc.Length) + i] = epc[i];
				for (int i = 0; i < pass.Length; i++)
					write_cmd[(8 - pass.Length) + i] = pass[i];
				Checksum(write_cmd, 21);
				networkstream.Write(write_cmd, 0, write_cmd.Length);
				networkstream.Read(aux, 0, aux.Length);
				/* Give the method what it needs dude! */
				return aux[3] == 0x00 ? true : false;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private bool ConfigReaderSys()
		{
			byte[] config_reader_sys = { 0x00, 0x04, 0x63, 0x02, 0x01, 0x04, 0x3b, 0xc1 };
			byte[] buff = new byte[6];

			try
			{
				networkstream.Write(config_reader_sys, 0, config_reader_sys.Length);
				buff = GivemeWhatINeed(0x63);
				return buff[3] == 0x00 ? true : false;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public override bool PowerAmplifier(string onoff, byte antenna)
		{
			byte[] off = { 0x00, 0x01, 0x61, 0x07, 0x46 };
			byte[] on = { 0x00, 0x02, 0x60, 0x01, 0x46, 0x28 };
			byte[] ack = new byte[6];

			try
			{
				switch (onoff)
				{
					case "on":
						on[3] = antenna;
						Checksum(on, 4);
						networkstream.Write(on, 0, on.Length);
						ack = GivemeWhatINeed(0x60);
						return ack[3] == 0x00 ? true : false;
					case "off":
						networkstream.Write(off, 0, off.Length);
						networkstream.Write(off, 0, off.Length);
						Clear();
						networkstream.Write(off, 0, off.Length);
						ack = GivemeWhatINeed(0x61);
						if (ack[3] == 0)
						{
							_loop = false;
							return true;
						}
						break;
				}
				return false;
			}
			catch (Exception ex)
			{
				throw ex;
			}

		}

		/* Give me or die. */
		private byte[] GivemeWhatINeed(byte mustbe)
		{
			// TODO: Usar buff[0] e buff[1] para o calculo do size
			byte[] header = new byte[3];
			byte[] buff = { 0xFF, 0xFF, 0xFF, 0xFF };
			byte[] tmp;
			int size = 0;
			long cnt = 0;
			try
			{
				do
				{
					if (networkstream.DataAvailable)
					{
						networkstream.Read(header, 0, 3);
						size = header[1] + 1;
						tmp = new byte[size];
						networkstream.Read(tmp, 0, size);
						buff = new byte[header.Length + size];
						System.Buffer.BlockCopy(header, 0, buff, 0, header.Length);
						System.Buffer.BlockCopy(tmp, 0, buff, 3, tmp.Length);
						cnt = 0;
					}
					else
					{
						cnt++;
					}
				} while (header[2] != mustbe && cnt < 3000000);
				return buff;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}
		private void Clear()
		{
			byte[] tmp = new byte[1024];
			while (networkstream.DataAvailable)
			{
				networkstream.Read(tmp, 0, 1024);
			}
		}

		private double SolveQ(int n) // para que serve este método?
		{
			return (System.Math.Ceiling((System.Math.Log((double)n) / System.Math.Log((double)2))));
		}
	}
}
